package aop.aspectj.AspectJExpressionPointcutAdvisor;

import java.util.Arrays;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;

@Aspect
public class CalculatorLoggingAspect {
	private Logger log = Logger.getLogger(getClass());
	
	@Pointcut("execution(* *.*(..))")
	private void loggingOperation(){}
	
//	@Before("execution(* example.samples.ArithmeticCalculator.add(..))")
	@Before("loggingOperation()")
	@Order(0)
	public void logBefore(){
		log.info("The method add() begins");
	}
	
//	@Before("execution(* *.*(..))")
	@Before("loggingOperation()")
	@Order(1)
	public void logBefore(JoinPoint joinPoint){
		log.info("Join point kind: " + joinPoint.getKind());
		log.info("Signature declaring type: " + joinPoint.getSignature().getDeclaringTypeName());
		log.info("Signature name: " + joinPoint.getSignature().getName());
		log.info("Arguments: " + Arrays.toString(joinPoint.getArgs()));
		log.info("Target class: " + joinPoint.getTarget().getClass().getName());
		log.info("This class: " + joinPoint.getThis().getClass().getName());
	}
	
//	@After("execution(* *.*(..))")
	@After("loggingOperation()")
	public void logAfter(JoinPoint joinPoint){
		log.info("The method " + joinPoint.getSignature().getName() + "() ends.");
	}
	
//	@AfterReturning("execution(* *.*(..))")
	@AfterReturning("loggingOperation()")
	public void logAfterReturning(JoinPoint joinPoint){
		log.info("The method " + joinPoint.getSignature().getName() + "() ends.");
	}
	
	@AfterReturning(
//			pointcut = "execution(* *.*(..))",
			pointcut = "loggingOperation()",
			returning = "result")
	public void logAfterReturning(JoinPoint joinPoint, Object result){
		log.info("The method " + joinPoint.getSignature().getName() + "() ends with " + result);
	}
	
//	@AfterThrowing("execution(* *.*(..))")
	@AfterThrowing("loggingOperation()")
	public void logAfterThrowing(JoinPoint joinPoint){
		log.info("An exception has been thrown in " + joinPoint.getSignature().getName() + "()");
	}
	
	@AfterThrowing(
//			pointcut = "execution(* *.*(..))",
			pointcut = "loggingOperation()",
			throwing = "e")
	public void logAfterThrowing(JoinPoint joinPoint, Throwable e){
		log.info("An exception " + e + " has been thrown in " + joinPoint.getSignature().getName() + "()");
	}
	
	@AfterThrowing(
//			pointcut = "execution(* *.*(..))",
			pointcut = "loggingOperation()",
			throwing = "e")
	public void logAfterThrowing(JoinPoint joinPoint, IllegalArgumentException e){
		log.info("Illegal argument  " + Arrays.toString(joinPoint.getArgs()) + " in " + joinPoint.getSignature().getName() + "()");
	}
	
//	@Around("execution(* *.*(..))")
	@Around("loggingOperation()")
	public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
		log.info("The method " + joinPoint.getSignature().getName() + "() begins with " + Arrays.toString(joinPoint.getArgs()));
		try {
			Object result = joinPoint.proceed();
			log.info("The method " + joinPoint.getSignature().getName() + "() ends with " + result);
			return result;
		} catch (IllegalArgumentException e) {
			log.info("Illegal argument  " + Arrays.toString(joinPoint.getArgs()) + " in " + joinPoint.getSignature().getName() + "()");
			throw e;
		}
	}
}
